import { think } from 'thinkjs';
// import RestModelClass, { RestOption } from "src/types/utils/rest";
import Base, { ServiceOption } from '../../../../../service/base';
import mongoose from 'mongoose';
import mongo from '../../../../../utils/mongo';
import { syncValidate } from '../../../../utils/validate';
import { $redis } from '../utils/redis';

import { IMessage, SchemaMessage } from '../../types/model/Message';
import {
  IServiceCloudImMsgDeleteParams,
  IServiceCloudImMsgFetchParams,
  IServiceCloudImMsgFetchResult,
  IServiceCloudImMsgSendParams,
  IServiceCloudImMsgSendResult,
} from '../../types/service/Message';
import UserModel from '../model/user';
import SessionModel from '../model/session';
import { ImConnectServiceClass } from './connect';

import { sendNotification } from '../utils/push';
import formatLastMsgText from '../../utils/formatLastMsgText';
import { IImDeviceEventMsgRcvAck } from '../../types';

export default class extends Base<IMessage> {
  constructor(option: ServiceOption) {
    super(option);
  }

  /**
   * 发送消息
   */
  async send(params: IServiceCloudImMsgSendParams): Promise<IServiceCloudImMsgSendResult> {
    await syncValidate(params, {
      msg: {
        required: true,
      },
    });
    let { account, token, msg } = params;
    const { to } = msg;
    // 获取相关信息
    const { users, usersMap } = await UserModel.getUserByAccount({
      accounts: [account, to],
    });
    console.log('[users] ', users, usersMap);
    if (users.length !== 2) throw new Err(500, '服务器错误#1');
    const fromUser = usersMap[account];
    const toUser = usersMap[to];
    // 数据校验
    await syncValidate(msg, {
      text: {
        required: true,
        enable: ({ type }) => type === 'text',
      },
      file: {
        required: true,
        type: 'object',
        enable: ({ type }) => type === 'audio' || type === 'image' || type === 'video',
      },
    });
    // 数据补充
    const now = Date.now();
    msg = {
      uniId: msg.scene + '_' + [account, to].slice().join('_'),
      ...msg,
      from: account,
      fromNick: fromUser.nick,
      time: now,
      userUpdateTime: now,
    };
    const MessageModel = mongo.model('message', SchemaMessage);
    const message = new MessageModel(msg);
    let { _id } = await message.save();
    if (think.isEmpty(_id)) {
      throw new Err('发送失败');
    }
    msg = {
      ...msg,
      idServer: _id,
    };
    // 更新会话
    const { session } = await SessionModel.createOrUpdateSession({
      scene: msg.scene,
      account,
      to,
      msg,
      unreadNum: 0,
    });
    // 推送至其他端
    const connectService = think.service('cloudim_connect') as ImConnectServiceClass;
    const { online } = await connectService.sendMessageToClient({
      account,
      message: {
        type: 'msg',
        msg,
        session,
      },
    });
    // 转接收流程
    this.receive({ msg });
    return {
      msg,
      session,
    };
  }

  async receive({ msg }: { msg: IMessage }) {
    const { from, to } = msg;
    try {
      // 更新会话
      const { session } = await SessionModel.createOrUpdateSession({
        scene: msg.scene,
        account: to,
        to: from,
        msg,
        unread: true,
      });
      // 判断终端是否在线
      const connectService = think.service('cloudim_connect') as ImConnectServiceClass;
      const { online } = await connectService.sendMessageToClient({
        account: to,
        message: {
          type: 'msg',
          msg,
          session,
        },
      });
      if (online) {
        let key = 'imack_' + (msg._id || msg.idServer);
        console.log('[key] ', key);
        const writeRedisRes = await $redis.set(key, 'yes', 'EX', 10);
        console.log('[writeRedisRes] ', writeRedisRes);
        setTimeout(async () => {
          const readRedisRes = await $redis.get(key);
          console.log('[readRedisRes] ', readRedisRes);
          // 消息未被接收并回执 判断其并未在线 发送推送通知
          if (!think.isEmpty(readRedisRes)) {
            // 推送
            this.pushNotice({msg});
          }
        }, 1000);
      } else {
        this.pushNotice({msg});
      }
    } catch (e) {
      console.log('[eee] ', e);
    }
  }

  async fetch({ from, to, limit = 50, begintime, endtime, reverse }: IServiceCloudImMsgFetchParams): Promise<IServiceCloudImMsgFetchResult> {
    console.log('[endtime] ', endtime);
    const MessageModel = mongo.model<IMessage & mongoose.Document>('message', SchemaMessage);
    let where = {
      $or: [
        { from, to },
        { from: to, to: from },
      ],
      time: { $lt: endtime },
      hides: { $ne: from },
    };
    const messages = await MessageModel.find(where)
      .sort({ time: reverse === 1 ? -1 : 1 })
      .limit(limit)
      .exec();
    const messagesCount = await MessageModel.find(where).count().exec();
    const have = messagesCount - messages.length;
    return {
      msgs: messages,
      size: messagesCount,
      have: have > 0 ? have : 0,
    };
  }

  async deleteMsg({ account, token, id }: IServiceCloudImMsgDeleteParams): Promise<'ok'> {
    const MessageModel = mongo.model<IMessage & mongoose.Document>('message', SchemaMessage);
    const res = await MessageModel.update(
      {
        _id: id,
      },
      {
        $addToSet: { hides: account },
      },
    );
    console.log('[deleteMsg] ', res);
    return 'ok';
  }

  async onRsgRcvAck({ account, client, msg }: IImDeviceEventMsgRcvAck): Promise<'ok'> {
    let key = 'imack_' + (msg._id || msg.idServer);
    await $redis.del(key);
    console.log('[del key] ', key);
    return 'ok';
  }

  async pushNotice({
    msg
  }: {
    msg: IMessage;
  }) {
    const account = msg.to;
    const { user } = await UserModel.findOneUser({ account, field: 'pushid' });
    console.log('[user] ', user);
    if (user.pushid) {
      const [pushid, os] = user.pushid.split('|');
      sendNotification({
        title: msg.fromNick,
        body: formatLastMsgText(msg),
        pushid,
        os: os as 'ios' | 'android'
      });
    }
  }

}
